//file voltmeter.c
//rms voltmeter
//runs on ere co embmega32 16Mhz 38400

//Feb 18 11 Bob G 

#include <iom32v.h>
#include <stdio.h>  //cprintf
#include <stdlib.h> //rand, abs
#include <string.h> //memset
#include <ctype.h>  //toupper
#include <macros.h> //stackcheck

#define INTR_OFF() asm("CLI")
#define INTR_ON()  asm("SEI")

//---ascii equates----------------
#define ESC  0x1b

//------vars in bss-------
char junk;
unsigned char dispon;
unsigned char os100ms;
unsigned char os1sec;
unsigned char hr,min,sec;
//unsigned int addat[8];  //internal 10 bit
unsigned int tics,ticsl;
int os100msaccum;
int os1secaccum;
int deltatms;
int deltatmsoff;
float volts,amps;
float range,gain;

//------rom---------
__flash unsigned char banner[]={"voltmeter Feb 18 11\n"};

//--------------------
void port_init(void){

  PORTA = 0x00; //pullups off
  DDRA  = 0x00; //inputs
 
  PORTB = 0xFF;
  DDRB  = 0xFE;
 
  PORTC = 0xFF; 
  DDRC  = 0xFF;
 
  PORTD = 0xFF;
  DDRD  = 0xFF;
}

//--------------------
//UART0 initialize
// desired baud rate: 38400
// actual: baud rate:38462 (0.2%)
// char size: 8 bit
// parity: Disabled
void uart0_init(void){
  UCSRB = 0x00; //disable while setting baud rate
  UCSRA = 0x00;
  UCSRC = BIT(URSEL) | 0x06;
  UBRRL = 0x19; //set baud rate lo 103dec=9600  0x19=38400@16
  UBRRH = 0x00; //set baud rate hi
  UCSRB = 0x18;
}

#if 0
//---------------------
//SPI initialisation
// clock rate: 115200hz
void spi_init(void){
  SPCR = 0x53; //setup SPI (slowest clock rate! 0x50 fastest)
  SPSR = 0x00; //setup SPI
}
#endif

//--------------------
//TIMER0 initialisation - prescale:64
// WGM: Normal
// desired value: 1mSec
// actual value:  1.000mSec (0.0%)
void timer0_init(void){
  TCCR0 = 0x00; //stop
  TCNT0 = 0x06; //set count
  OCR0  = 0xFA;  //set compare
  TCCR0 = 0x03; //start timer
}

#if 0
//--------------------
//TIMER1 initialisation - prescale:1
// WGM: 0) Normal, TOP=0xFFFF
// desired value: 31372Hz
// actual value: 31372.549Hz (0.0%)
void timer1_init(void){
  TCCR1B = 0x00; //stop
  TCNT1H = 0xFE; //setup
  TCNT1L = 0x02;
  OCR1AH = 0x01;
  OCR1AL = 0xFE;
  OCR1BH = 0x01;
  OCR1BL = 0xFE;
  ICR1H  = 0x01;
  ICR1L  = 0xFE;
  TCCR1A = 0xA0;
  TCCR1B = 0x01; //start Timer
}
#endif

//--------------------
void adc_init(void){
//ADC initialize

  ADCSR = 0x87;
}

//--------------------
void init_devices(void){

  CLI(); //disable all interrupts
  port_init();
  uart0_init();
  adc_init();
  timer0_init();
// timer1_init();
// spi_init();

  MCUCR = 0x00;
  GICR  = 0x00;
  TIMSK = 0x02; //timer interrupt sources t0 oc
  SEI(); //re-enable interrupts
}

//-------------------
void delnms(int n){
//delay n ms
int x;

  while(n--){
//    x=2400;       //empirically determined fudge factor  14 mhz
    x=2600;       //empirically determined fudge factor  16 mhz
//    x=2800;       //empirically determined fudge factor  18 mhz
//    x=3000;       //empirically determined fudge factor  20 mhz
    while(x--);
  }
}

//-------------------------
void initvars(void){
//init vars

  volts=1;
	amps=0;
	range=1.0;
	gain=3.0/1023.0;
}

//------RS232-------------------
unsigned char kbhit(void){
//return nonzero if char waiting  polled version
unsigned char b;

  b=0;
  if(UCSRA & (1<<RXC)) b=1;
  return b;
}

//-----------------------
int getchar(void){
//polled version... 
unsigned char c;

  while(!kbhit()){}; //wait for char
  c=UDR;             //get char from usart
  return c;  
}

#if 0
//----------------------------
#define RXBUFSIZ 200
unsigned char rxbuf[RXBUFSIZ]; 
unsigned char rxindx,rxondx;   

//-------------------------
unsigned char kbhit(void){
//return nonzero if char waiting  intr version
unsigned char b;

  b=rxindx != rxondx;
  return b;
}

//-----------------------
int getchar(void){
//intr version... overrides version in library
char c;

  while(!kbhit()){}; //wait for char
  c=rxbuf[rxondx++]; //get char from rxbuf
  if(rxondx >= RXBUFSIZ) rxondx=0;
  return c;  
}

//------------------------------
#pragma interrupt_handler uart0_rx_isr:iv_USART0_RX
void uart0_rx_isr(void){
//uart has received a character in UDR
char c;

  c=UDR;            //get char from usart
  rxbuf[rxindx++]=c; //put char in rxbuf
  if(rxindx >= RXBUFSIZ) rxindx=0;  
}
#endif

//-----------------------
int putc(char c){
//put char to usart0... dont add lf after cr

  while((UCSRA & 0x20)==0){};	//0x20 is UDRE0. ==0 means data register full
  UDR=c; //send char
  return c;
}

//----------------------  
int putchar(char c)	{
//adds lf after cr

  if(c=='\n'){
    putc('\r');
  }  
  putc(c);
  return c;
}

//--------------------
void crlf(void){
//send cr lf

  putchar('\n');
}

//------------------
void space(void){
//send space

  putc(' ');
}
  
//------------------
void spaces(char n){
//send n spaces

  while(n--)
    space();
}
  
#define ESC 0x1b
//----------------------  
void gotoxy(int x, int y){
//ansi cursor positioning sequence E[y;xH

  putc(ESC);
  putc('[');
  cprintf("%d",y);
  putc(';');
  cprintf("%d",x);
  putc('H');
}

//----------------------
void clrscr(void){
//clear ansi terminal screen

  putc(ESC);
  putc('[');
  putc('2');
  putc('J');
}

//----------------------
void homecrsr(void){
//home cursor

  putc(ESC);
  putc('[');
  putc('f');
}

//------------------
void initscreen(void){
//clear screen and print banner

  clrscr();
	homecrsr();
  cprintf(banner);
}

//-------------------------
unsigned char getche(void){
//get and echo a char from rs232
char c;

  c=getchar();
  putchar(c);
  return(c);
}

//-------------------------
unsigned char gethex(void){
//return a hex char from rs232
unsigned char b,c;

  b=0xff; //error return value
  c=toupper(getche());
  if(isxdigit(c)) b=c-0x30; //if c ok, cvt ascii digit to binary
  if((c>='A') && (c<='F')) b-=7;     //if c hexcvt ascii A to binary 10
  return(b);
}

//---------------------------
unsigned char getbyte(void){
//get 2 nibbles, return a binary byte from rs232
unsigned char n1,n2;

  n1=gethex();
  n2=gethex();
  return((n1 << 4) + n2);
}

//----------------------------
unsigned int getaddr(void){
//return addr from rs232
unsigned int th,tl;

  th=getbyte();
  tl=getbyte();
  return((th << 8) + tl);
}

//------debug subs-----------
void dump256ram(unsigned char *n){
//dump 16 rows of 16 bytes
unsigned char r,c;
unsigned char *p, *pa, ch;

  p=n;
  crlf();
  spaces(6);
  for(c=0; c < 16; c++){
    cprintf("%02x ",c);      //header
    if(c==7) space();
  }
  crlf();
  crlf();
  for(r=0; r < 16; r++){
    cprintf("%04x  ",p);     //print addr  at beg of line
    pa=p;                   //remember p for ascii
    for(c=0; c < 16; c++){
      cprintf("%02x ",*p++); //print hex
      if(c==7) space();
    }
    for(c=0; c < 16; c++){
      ch=*pa++;
      if((ch > 0x20) && (ch !=0x0a) && (ch != 0x8a)) //if printing char
        putc(ch);   //print ascii
      else  
        putc('.'); 
      if(c==7) space();
    }
    crlf();
  }
}

//---------------------------------
void dump256rom(__flash unsigned char *n){
//dump 16 rows of 16 bytes
unsigned char r,c,ch;
__flash unsigned char *p, *pa;

  p=n;
  crlf();
  spaces(6);
  for(c=0; c < 16; c++){
    cprintf("%02x ",c);      //header
    if(c==7) space();
  }
  crlf();
  crlf();
  for(r=0; r < 16; r++){
    cprintf("%04x  ",p);     //print addr  at beg of line
    pa=p;                   //remember p for ascii
    for(c=0; c < 16; c++){
      cprintf("%02x ",*p++); //print hex
      if(c==7) space();
    }
    for(c=0; c < 16; c++){
      ch=*pa++;
      if((ch > 0x20) && (ch !=0x0a) && (ch != 0x8a)) //if printing char
        putc(ch);   //print ascii
      else  
        putc('.'); 
      if(c==7) space();
    }
    crlf();
  }
}

//------------------
void examineram(void){
//ask for mem range and dump
unsigned char *from;
unsigned char c;

  cprintf("from:");
  from=(unsigned char *)getaddr();
  while(c!='q'){
    dump256ram(from);
    cprintf("np or q...");
    c=getchar();
    if(c=='n') from+=0x100;
    if(c=='p') from-=0x100;
  }  
}

//------------------
void examinerom(void){
//ask for mem range and dump
__flash unsigned char *from;
unsigned char c;

  cprintf("from:");
  from=(__flash unsigned char *)getaddr();
  while(c!='q'){
    dump256rom(from);
    cprintf("np or q...");
    c=getchar();
    if(c=='n') from+=0x100;
    if(c=='p') from-=0x100;
  }  
}

//------------------
void deposit(void){
//ask for addr and data
unsigned char *at;
unsigned char c;
unsigned char nh,nl;

  cprintf("at:");
  at=(unsigned char *)getaddr();
  while(1){
    cprintf(" %02x ",*at);
    nh=gethex();
    if(nh==0xff) return;
    nl=gethex();
    if(nl==0xff) return;
    c=((nh << 4) | nl);
    *at++=c;
  }
}

//------------------
void fill(void){
//ask for mem range and fill char and fill
unsigned char *from, *to, with;

  cprintf("from:");
  from=(unsigned char *)getaddr();
  cprintf(" to:");
  to=(unsigned char *)getaddr();
  cprintf(" with:");
  with=getbyte();
  memset(from,with,to-from);
}

#if 0
//-----------------------------
void (* fn)(void); //fn prototype

void dojsr(void){
//ask for addr, jsr to it
__flash unsigned char *to;

  cprintf(" to:");
  to=(__flash unsigned char *)getaddr();
  fn=(void *)to;
  (*fn)();  //call function fn and returns
}
#endif

//------------------
void debugmenu(void){
//monitor type cmds

  cprintf("cmds:\n"
          " e examine\n"
          " d deposit\n"
          " f fill\n"
//          " j jsr\n"
          " q quit\n"
				);
}

//------------------
void debugloop(void){
//examine and deposit mem regs
char c;

  debugmenu();
  while(c != 'q'){
    crlf();
    cprintf("cmd:");
    c=getche();
    crlf();
    switch(c){
      case 'e': examineram(); break;
      case 'c': examinerom(); break;
      case 'd': deposit(); break;
      case 'f': fill();    break;
//      case 'j': dojsr();   break;
      default: debugmenu();
    }
  }
}
//----end of debug subs----------

//--------------------
void _StackOverflowed(char n){
//called from _Stackcheck()   must link this file first??

  INTR_OFF();
	if(n) putc('H'); else putc('S');
  while(1){};
}

__flash char spindat[4]={'-','\\','|','/'};
char spindex;
//------------------
void spin(void){

  spindex++;
  if(spindex > 3) spindex=0;
  cprintf("%c\r",spindat[spindex]);
}

#if 0
//----spi---------------------------
char readspi(void){
//return char from spi
char cnt,stat;

  cnt=0;
  do{
    stat=SPSR;
    cnt++;
    if(cnt==255){
      cprintf("spi rcv timeout ");
      break;
    }
  }while((stat & 0x80)==0);  //keep checking until done bit
  return(SPDR); //clears done flag
}

//--------------------------
void spiout8(char c){
//send c out spi
char cnt,stat;

  SPDR=c; //send data
  cnt=0;
  do{
    stat=SPSR;
	  cnt++;    
    if(cnt==255){
      cprintf("spi xmt timeout! ");
      break;
    }
  }while((stat & 0x80)==0);
}
#endif

//---------------------
unsigned int readadchan(char n){
//read ch n of internal a/d  10 bit unsigned

  ADMUX= n;        //select channel n
  ADCSRA |=0x40;   //init conversion
  while((ADCSRA & 0x40) !=0){}; //wait for conv complete
  return ADC;
}

//--------------------
void readadloop(void){
//read internal 10 bit ad 0-0x3ff  0..1023
char c,n;
unsigned int tmp;

  cprintf("a/d\n");
  cprintf("0    1    2    3    4    5    6    7 \n");
  c=0;
  while(c!='q'){
    if(kbhit()){
      c=getchar();
    }  
	  for(n=0; n < 8; n++){
      tmp=readadchan(n);
      cprintf("%4d ",tmp);
	  }
  	cprintf("\r");
		delnms(5);  
  }
}

unsigned int ad;        //sample 0-1023
unsigned int addat[256]; //samples
unsigned int addatsq[256]; //samples squared
unsigned long int tot;   
unsigned int adavg;
unsigned int adrms;
//unsigned int tdem;
unsigned int sndx;     //sample index
//------------------------
unsigned int calcavg(unsigned int n){
//return avg of last 256 samps

  tot -= addat[sndx];    //subtract off old
	tot += n;              //add in new
  addat[sndx]=n;         //save samp
	sndx++;                //bump sndx
	if(sndx >= 255) sndx=0; //rewind
	adavg=tot >> 8;        //calc avg
	return adavg;
}

//------------------------
unsigned int calcrms(unsigned int n){
//return rms of last 256 samps
unsigned int sampsq;

  tot -= addatsq[sndx];   //subtract off old
	sampsq=n*n;             //calc sample squared
	tot += sampsq;          //add in new
  addatsq[sndx]=sampsq;   //save sampsq
	sndx++;                 //bump sndx
	if(sndx >= 255) sndx=0; //rewind
	adrms=tot >> 8;         //calc rms
	return adrms;           //return rms
}

//---------------------
#pragma interrupt_handler timer0_comp_isr:iv_TIMER0_COMP
void timer0_comp_isr(void){
//compare occured TCNT0=OCR0   1ms

  tics++;
}

//----------------------
void udtime(void){
//read tics, generate oneshots, update hr,min,sec
unsigned int tmptics;

  tmptics=tics;           //read copy in case int hits
  deltatms=tmptics-ticsl; //ms since last pass
  ticsl=tmptics;          //remember last pass

  os100msaccum+=deltatms;
  os100ms=0;
  if(os100msaccum >= 100){
    os100ms=1;
    os100msaccum -= 100;
  }	  
  os1secaccum+=deltatms; //accumulate milliseconds
  os1sec=0;
  if(os1secaccum >= 1000){
    os1sec=1;
	  os1secaccum -= 1000;
  }
  if(os1sec){
    sec++;
	  if(sec > 59){
	    min++;
	    sec=0;
	    if(min > 59){
	      hr++;
	    	min=0;
		    if(hr > 23){
		      hr=0;
		    }//hr
	    }//min
	  }//sec
  }//os1sec	  	  
}

//-----------------
void calcvolts(void){

  volts=adrms*gain;
}

//-----------------
void vmmenu(void){
  cprintf(" v volts  a amps  3 300.0  2 30.0  1 3.00\n"
	);
}

//-----------------
void voltmeterloop(void){
//voltmeterloop
char c;
unsigned int tmp;

  c=' ';
  dispon=1;
//  cprintf("voltmeter     q to quit\n");
	vmmenu();
//  cprintf("hr:mn:sc  volts \n");
  while(c != 'q'){    //loop forever
    if(kbhit()){      //if char waiting from pc
	    c=getchar();    //get the char
	    if(c=='v') {
			  volts=1;
				amps=0;
			}	
	    if(c=='a') {
			  volts=0;
				amps=1;
			}	
			if(c=='1'){
			  range=3.0;
				gain=3.0/1023.0;
			}
			if(c=='2'){
			  range=30.0;
				gain=30.0/1023.0;
			}
			if(c=='3'){
			  range=300.0;
				gain=300.0/1023.0;
			}
	    if(c=='p') dispon= !dispon;
    } 
	  udtime();
    tmp=readadchan(0); //0-1023
//    adavg=calcavg(tmp);
    adrms=calcrms(tmp);
		calcvolts();
	 
    if(!dispon) deltatmsoff=deltatms;
    if(dispon){
//  	  gotoxy(1,15);
      cprintf("%02d:%02d:%02d  %#7.3f %4d %4d \r",hr,min,sec,volts,deltatms,deltatmsoff);
    	delnms(5);
	  }
    _StackCheck();
  } //while 1
}

//--------------------------
void menu(void){
//cmds

  cprintf("cmds:\n");
  cprintf(" a a/d\n");
  cprintf(" m monitor cmds\n");
  cprintf(" v voltmeterloop\n");
}

//-----------------
void main(void){
//main program
char c;

  init_devices();
  initvars();
  initscreen(); //banner
	voltmeterloop();
  while(1){     //main loop
    menu();
    crlf();
    putc('>'); //prompt for input
    c=getche();
    crlf();
    switch(c){
      case 'a': readadloop();    break;
      case 'm': debugloop();     break;
      case 'v': voltmeterloop(); break;
      default: menu();
    }
    _StackCheck();
  }//while
}

//---strings embedded in rom--------------
__flash char copyrightstring[]={"voltmeter computer code\
written by bobgardner at aol.com"};
//--------eof-------------

